/* 
herein are functions called in response to a left button drag
or right click in window "board", in mode Play.
*/

#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "event.h"
#include "werr.h"
#include "bbc.h" 
#include "coords.h" 

#include <stdlib.h>

#include "Polysaw.h"

/* data structures for dragging */
static  DragState        drag;
static  Polymo           dragged;


/************************ FUNCTIONS ************************/


static  void    flip_piece(void);
static  void    set_drag_state(coords_pointstr w, coords_pointstr m);
static  void    start_drag(void);
static  void    start_move_drag(void);
static  void    start_spin_drag(void);
static  void    update_move_drag(void);
static  void    update_spin_drag(void);
static  void    put_down_after_move(int wrk_x, int wrk_y);
static  void    put_down_after_spin(coords_pointstr mse);
static  void    eor_draw_moving_piece(int x, int y);
static  void    eor_draw_spinning_piece(coords_pointstr);

static  void    put_down_poly(void);
static  BOOL    room_for_dragged(void);
static  void    find_spin_angle(int *sin, int *cos, coords_pointstr origin,                                    coords_pointstr from, coords_pointstr to);


/*_____________________________________________________________*/










void deal_with_play_but(coords_pointstr wrk, coords_pointstr mse,                                           wimp_bbits mse_b, wimp_box box)
{
wimp_dragstr    dragstr;          

if (mse_b & wimp_BRIGHT)  
   {
   if (which_piece(wrk) < MAX_PIECES)
     {
     drag.piece = which_piece(wrk);
     dragged = piece[which_piece(wrk)];                        
     flip_piece();
     }
   }

if (mse_b & wimp_BDRAGLEFT)
   {
   set_drag_state(wrk, mse);

   dragstr.window = board_wh;
   dragstr.type = 7;
   dragstr.parent = box;
   wimpt_noerr(wimp_drag_box(&dragstr));
   start_drag();

   }
}


void flip_piece(void)
{
int i;

for (i=0; i<dragged.nofunits; i++) 
  {
  dragged.units[i].x = -dragged.units[i].x;
  }
dragged.rotflipstate = 9 - dragged.rotflipstate;

put_down_poly();
}




void set_drag_state(coords_pointstr w, coords_pointstr m)
{
int p, i;
coords_pointstr pt;

drag.start = m;
drag.old = m;

for (p=0; p < nofpieces; p++)
  {
  pt.x = w.x - piece[p].cog.x;
  pt.y = w.y - piece[p].cog.y;
  /* pt is offset to centre of piece, as all piece info is rel. cog */
  if ( coords_withinbox(&pt, &piece[p].box) )   
     {
     for (i=0; i < piece[p].nofunits; i++)
        {
        if ( abs(pt.x - piece[p].units[i].x) <= HS   &&   
             abs(pt.y - piece[p].units[i].y) <= HS )   
             {
             drag.piece = p;
             dragged = piece[p];
             if (  (pt.x)*(pt.x) + (pt.y)*(pt.y) < piece[p].movespin_rad  )
                 drag.kind = 1; 
             else
                 drag.kind = -1;
             return;
             }
        }
     }
  }
drag.kind = 0;
drag.piece = MAX_PIECES+1;
return;
}


void start_drag(void)
{
event_setmask((wimp_emask)0);
if (drag.kind ==  1) start_move_drag(); 
if (drag.kind == -1) start_spin_drag();
}



void start_move_drag(void)
{
wimp_redrawstr r;
int more;

r.w = board_wh;
r.box = board_windefn->ex;
wimpt_noerr(wimp_update_wind(&r, &more));

while (more)
  {
  eor_draw_moving_piece(drag.old.x, drag.old.y);
  wimpt_noerr(wimp_get_rectangle(&r, &more));
  } 
} 



void start_spin_drag(void)
{
wimp_redrawstr r;
int more;
int i;

/* fill in outline defns in drag.lines[]. */

for (i=0; i < piece[drag.piece].nofunits; i++)
  {
  drag.lines[4*i+0].x0 = piece[drag.piece].units[i].x + HS;
  drag.lines[4*i+0].y0 = piece[drag.piece].units[i].y + HS;
  drag.lines[4*i+0].x1 = piece[drag.piece].units[i].x + HS;
  drag.lines[4*i+0].y1 = piece[drag.piece].units[i].y - HS;

  drag.lines[4*i+1].x0 = piece[drag.piece].units[i].x + HS;
  drag.lines[4*i+1].y0 = piece[drag.piece].units[i].y - HS;
  drag.lines[4*i+1].x1 = piece[drag.piece].units[i].x - HS;
  drag.lines[4*i+1].y1 = piece[drag.piece].units[i].y - HS;

  drag.lines[4*i+2].x0 = piece[drag.piece].units[i].x - HS;
  drag.lines[4*i+2].y0 = piece[drag.piece].units[i].y - HS;
  drag.lines[4*i+2].x1 = piece[drag.piece].units[i].x - HS;
  drag.lines[4*i+2].y1 = piece[drag.piece].units[i].y + HS;

  drag.lines[4*i+3].x0 = piece[drag.piece].units[i].x - HS;
  drag.lines[4*i+3].y0 = piece[drag.piece].units[i].y + HS;
  drag.lines[4*i+3].x1 = piece[drag.piece].units[i].x + HS;
  drag.lines[4*i+3].y1 = piece[drag.piece].units[i].y + HS;
  }


r.w = board_wh;
r.box.x0 = piece[drag.piece].cog.x + 3*piece[drag.piece].box.x0/2;
r.box.y0 = piece[drag.piece].cog.y + 3*piece[drag.piece].box.y0/2;
r.box.x1 = piece[drag.piece].cog.x + 3*piece[drag.piece].box.x1/2;
r.box.y1 = piece[drag.piece].cog.y + 3*piece[drag.piece].box.y1/2;

wimpt_noerr(wimp_update_wind(&r, &more));

while (more)
  {
  eor_draw_spinning_piece(drag.start);
  wimpt_noerr(wimp_get_rectangle(&r, &more));
  } 
}


void update_drag(void)
{
if (drag.kind ==  1) update_move_drag();  
if (drag.kind == -1) update_spin_drag();  
}




void  update_move_drag(void)
{
wimp_mousestr  m;
wimp_redrawstr r;
int more;
int wrk_x, wrk_y;

wimpt_noerr(wimp_get_point_info(&m));

r.w = board_wh;
r.box = board_windefn->ex;

if (m.bbits) 
  {
  drag.new.x = m.x;
  drag.new.y = m.y;
  
  wimpt_noerr(wimp_update_wind(&r, &more));
  while (more)
    {
    eor_draw_moving_piece(drag.old.x, drag.old.y);
    eor_draw_moving_piece(drag.new.x, drag.new.y);

    wimpt_noerr(wimp_get_rectangle(&r, &more));
    }
  drag.old = drag.new;
  }
else
  {        
  wimpt_noerr(wimp_update_wind(&r, &more));

  while (more)
    {
    eor_draw_moving_piece(drag.old.x, drag.old.y);           
    wimpt_noerr(wimp_get_rectangle(&r, &more));
    }

  wrk_x = coords_x_toworkarea(drag.old.x, (coords_cvtstr *)&r.box);
  wrk_y = coords_y_toworkarea(drag.old.y, (coords_cvtstr *)&r.box);

  put_down_after_move(wrk_x, wrk_y);
  }
}



void update_spin_drag(void)
{
wimp_mousestr  m;
wimp_redrawstr r;
int more;
int wrk_x, wrk_y;

wimpt_noerr(wimp_get_point_info(&m));

r.w = board_wh;
r.box.x0 = piece[drag.piece].cog.x + 3*piece[drag.piece].box.x0/2;
r.box.y0 = piece[drag.piece].cog.y + 3*piece[drag.piece].box.y0/2;
r.box.x1 = piece[drag.piece].cog.x + 3*piece[drag.piece].box.x1/2;
r.box.y1 = piece[drag.piece].cog.y + 3*piece[drag.piece].box.y1/2;

if (m.bbits) 
  {
  drag.new.x = m.x;
  drag.new.y = m.y;
  
  wimpt_noerr(wimp_update_wind(&r, &more));
  while (more)
    {
    eor_draw_spinning_piece(drag.old);
    eor_draw_spinning_piece(drag.new);

    wimpt_noerr(wimp_get_rectangle(&r, &more));
    }
  drag.old.x = drag.new.x;
  drag.old.y = drag.new.y;
  }
else
  {    
  wimpt_noerr(wimp_update_wind(&r, &more));

  while (more)
    {
    eor_draw_spinning_piece(drag.old);           
    wimpt_noerr(wimp_get_rectangle(&r, &more));
    }

  wrk_x = coords_x_toworkarea(drag.old.x, (coords_cvtstr *)&r.box);
  wrk_y = coords_y_toworkarea(drag.old.y, (coords_cvtstr *)&r.box);

  put_down_after_spin(drag.old);
  }
}






void eor_draw_moving_piece(int x, int y)
{
int i;

wimpt_noerr(wimp_setcolour( (0<<7) | (3<<4) | 2));

for (i=0; i < piece[drag.piece].nofunits; i++)
      {
        wimpt_noerr(bbc_rectangle(x + piece[drag.piece].units[i].x - HS,  y + piece[drag.piece].units[i].y - HS,  2*HS-1,  2*HS-1));
      }

}


void eor_draw_spinning_piece(coords_pointstr mse)
{
int             sin, cos;       /* use binary point at bit 12 */
int             a, b, x0, y0, x1, y1, i;
coords_pointstr cog;
wimp_wstate     winstate;

wimpt_noerr(wimp_get_wind_state(board_wh, &winstate));

cog = piece[drag.piece].cog;
coords_point_toscreen(&cog, (coords_cvtstr *)&winstate.o.box);

/* now  cog, drag.start, mse are in screen coords */

find_spin_angle(&sin, &cos, cog, drag.start, mse); 
 
/* draw rotated lines */
for (i = 0; i < 4*(piece[drag.piece].nofunits); i++)
  {
  a = drag.lines[i].x0;
  b = drag.lines[i].y0;
  x0 = (int) ( ((a*cos)>>12) - ((b*sin)>>12) ) + cog.x;
  y0 = (int) ( ((a*sin)>>12) + ((b*cos)>>12) ) + cog.y;
  a = drag.lines[i].x1;
  b = drag.lines[i].y1;
  x1 = (int) ( ((a*cos)>>12) - ((b*sin)>>12) ) + cog.x;
  y1 = (int) ( ((a*sin)>>12) + ((b*cos)>>12) ) + cog.y;

  wimpt_noerr(wimp_setcolour( (0<<7) | (3<<4) | 2));
  wimpt_noerr(bbc_move(x0, y0));
  wimpt_noerr(bbc_draw(x1, y1));
  }

}





void put_down_after_move(int wrk_x, int wrk_y)
{
int cogmodgrid;

cogmodgrid = dragged.cog.x % (2*HS);
  dragged.cog.x  =  roundtonearest(wrk_x - cogmodgrid, 2*HS) +                                                               cogmodgrid;
cogmodgrid = dragged.cog.y % (2*HS);
  dragged.cog.y  =  roundtonearest(wrk_y - cogmodgrid, 2*HS) +                                                               cogmodgrid;
put_down_poly();
}


void put_down_after_spin(coords_pointstr mse)
{
int             sin, cos;
int               i, ang, qtsin, qtcos;
coords_pointstr   cog;
wimp_wstate       winstate;

wimpt_noerr(wimp_get_wind_state(board_wh, &winstate));

cog = dragged.cog;
coords_point_toscreen(&cog, (coords_cvtstr *)&winstate.o.box);

find_spin_angle(&sin, &cos, cog, drag.start, mse); 

if (   abs(sin) < (int) (0.7*4096)   ) 
  {
  qtsin = 0;
  if (cos < 0)
     { 
     qtcos = -1;
     ang=4;
     }
  else
     {
     qtcos = 1;
     ang=0;
     }
  }
else
  {
  qtcos = 0;
  if (sin < 0)
     {
     qtsin = -1;
     ang=6;
     }
  else
     {
     qtsin = 1;
     ang=2;
     }
  }

if (ang==2 || ang==6)
  {
  int xfidget, yfidget;
  xfidget = dragged.cog.x % (2*HS);
  yfidget = dragged.cog.y % (2*HS);
  dragged.cog.x = roundtonearest(dragged.cog.x, 2*HS) + yfidget;
  dragged.cog.y = roundtonearest(dragged.cog.y, 2*HS) + xfidget;
  }


dragged.rotflipstate += ang;
dragged.rotflipstate &= 7;
for (i=0; i < dragged.nofunits; i++)
  {
  int x, y;

  x = dragged.units[i].x;
  y = dragged.units[i].y;
  dragged.units[i].x = x*qtcos - y*qtsin;
  dragged.units[i].y = x*qtsin + y*qtcos;

  }

put_down_poly();
}



void put_down_poly(void)
{
int i, p, x, y;

p= drag.piece;      

event_setmask((wimp_emask)1);

/* delete poly from grid[][] */ 
for (i=0; i<piece[p].nofunits; i++)
  {
    x = piece[p].cog.x + piece[p].units[i].x;
    y = piece[p].cog.y + piece[p].units[i].y;
    grid[(-y-HS)/(2*HS)][(x-HS)/(2*HS)] &= ~PIECE_BIT;
  }

if (room_for_dragged())
  {
  force_piece_redraw(drag.piece);   /* clear old position */
  piece[drag.piece] = dragged;
  force_piece_redraw(drag.piece);   /* draw new posn */
  }
else
  bbc_vdu(7);

/* put poly back in grid, in new or old posn */

for (i=0; i<piece[p].nofunits; i++)
  {
    x = piece[p].cog.x + piece[p].units[i].x;
    y = piece[p].cog.y + piece[p].units[i].y;
    grid[(-y-HS)/(2*HS)][(x-HS)/(2*HS)] |= PIECE_BIT;
  }
drag.kind = 0;
}


BOOL    room_for_dragged(void)
{
int x,y,g,i, row, col;
for (i=0; i<dragged.nofunits; i++)
  {
    x = dragged.cog.x + dragged.units[i].x;
    y = dragged.cog.y + dragged.units[i].y;
    col = (x-HS)/(2*HS);
    row = (-y-HS)/(2*HS);
    if (col<0 || row<0 || col>=MAX_BOARD || row>=MAX_BOARD)
           return FALSE;
    g = grid[row][col];
    if ( g & (PIECE_BIT | SQUARE_BIT) )
           return FALSE;
  }
return TRUE;
}



void  find_spin_angle(int *sin, int *cos, coords_pointstr origin,                                    coords_pointstr from, coords_pointstr to)
{
int c, d;
int fx, fy, tx, ty;

/*  a bit of trig.
*
*        origin
*           *---------------to
*            \ angle
*             \
*              \
*               \
*                \
*                 \from
*/ 

fx = from.x - origin.x;
fy = from.y - origin.y;
tx = to.x - origin.x;
ty = to.y - origin.y;

c = integer_sqrt(tx*tx + ty*ty);
d = integer_sqrt(fx*fx + fy*fy);

d = c*d;
if (c <= 8)
  {
  *sin = 4096;
  *cos = 0;
  }
else
  { 
  *sin = ((fx*ty - fy*tx)<<12)/d;
  *cos = ((fx*tx + fy*ty)<<12)/d;
  }
} 



